#pragma once

#include <iostream>
#include <memory>
#include <unordered_map>
#include "Connection.hpp"
#include "Epoller.hpp"
#include "Comm.hpp"

class Reactor
{
    const static int gnum = 64;

public:
    Reactor() : _epoller(std::make_unique<Epoller>()), _isrunning(false)
    {
    }

    void AddConnection(int fd, uint32_t events, InetAddr& addr, int type)
    {
        Connection* conn = new Connection(fd);
        if(conn == nullptr) 
        {
            LOG(FATAL, "new error!\n");
            exit(0);
        }
        conn->SetType(type);
        conn->_R = this;
        conn->_addr = addr;
        conn->SetEvents(events);
        if(type == NormalConnection)
        {
            conn->RegistHandler(_OnRecver, _OnSender, _OnExcepter);
        }
        else
        {
            conn->RegistHandler(_OnConnect, nullptr, nullptr);
        }

        if(!_epoller->AddEvent(conn->Sockfd(), conn->Events())) 
            return;

        _connections.insert(std::make_pair(fd, conn));
    }

    void LoopOnce(int timeout)
    { 
        int n = _epoller->Wait(revs, gnum, timeout);
        LOG(DEBUG, "Get %d message!\n", n);
        for (int i = 0; i < n; i++)
        {
            uint32_t revents = revs[i].events;
            int fd = revs[i].data.fd;
            LOG(DEBUG, "This is %d  Events:%s\n", fd, EventsToString(revents).c_str());
            if(revents & EPOLLHUP) revents |= (EPOLLIN | EPOLLOUT);
            if(revents & EPOLLERR) revents |= (EPOLLIN | EPOLLOUT);

            if(revents & EPOLLIN)
            {
                if(IsConnectionExists(fd) && _connections[fd]->_handler_recver)
                    _connections[fd]->_handler_recver(_connections[fd]);
            }
            if(revents & EPOLLOUT)
            {
                if(IsConnectionExists(fd) && _connections[fd]->_handler_sender)
                {
                    _connections[fd]->_handler_sender(_connections[fd]);
                }
            }
        }
    }

    void DelConnection(int sockfd)
    {
        if(!IsConnectionExists(sockfd)) return;

        EnableConnrctionReadWrite(sockfd, 0, 0);
        _epoller->DelEvent(sockfd);
        _connections[sockfd]->Close();

        delete _connections[sockfd];
        _connections.erase(sockfd);
    } 

    void EnableConnrctionReadWrite(int fd, bool readable, bool writeable)
    {
        if(!IsConnectionExists(fd)) return;
        uint32_t events = ((readable? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _connections[fd]->SetEvents(events);

        _epoller->ModEvent(fd, events);
    }

    bool IsConnectionExists(int fd)
    {
        return _connections.find(fd) != _connections.end();
    }

    void Dispatcher()
    {
        int timeout = -1;
        _isrunning = true;
        
        while (1)
        {
            LoopOnce(timeout);
            PrintDebug();
        }
        _isrunning = false;
    }
    void SetConnect(handler_t Connect)
    {
        _OnConnect = Connect;
    }
    void SetNormalHandler(handler_t Recv, handler_t Send, handler_t Except)
    {
        _OnExcepter = Except;
        _OnRecver = Recv;
        _OnSender = Send;
    }
    void PrintDebug()
    {
        std::string fd_list;
        for(auto& [fd, conn] : _connections)
        {
            fd_list+= std::to_string(fd) + " ";
        }
        std::cout<<"fd_list: "<<fd_list<<std::endl;
    }
    ~Reactor()
    {
    }

private:
    std::unordered_map<int, Connection*> _connections;   
    std::unique_ptr<Multiplex> _epoller;
    epoll_event revs[gnum];
    bool _isrunning;

    handler_t _OnConnect;
    handler_t _OnRecver;
    handler_t _OnSender;
    handler_t _OnExcepter;
};